# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt


import json

import frappe
from frappe import _, scrub
from frappe.utils import flt


def execute(filters=None):
	return IssueSummary(filters).run()

class IssueSummary(object):
	def __init__(self, filters=None):
		self.filters = frappe._dict(filters or {})

	def run(self):
		self.get_columns()
		self.get_data()
		self.get_chart_data()
		self.get_report_summary()

		return self.columns, self.data, None, self.chart, self.report_summary

	def get_columns(self):
		self.columns = []

		if self.filters.based_on == 'Customer':
			self.columns.append({
				'label': _('Customer'),
				'options': 'Customer',
				'fieldname': 'customer',
				'fieldtype': 'Link',
				'width': 200
			})

		elif self.filters.based_on == 'Assigned To':
			self.columns.append({
				'label': _('User'),
				'fieldname': 'user',
				'fieldtype': 'Link',
				'options': 'User',
				'width': 200
			})

		elif self.filters.based_on == 'Issue Type':
			self.columns.append({
				'label': _('Issue Type'),
				'fieldname': 'issue_type',
				'fieldtype': 'Link',
				'options': 'Issue Type',
				'width': 200
			})

		elif self.filters.based_on == 'Issue Priority':
			self.columns.append({
				'label': _('Issue Priority'),
				'fieldname': 'priority',
				'fieldtype': 'Link',
				'options': 'Issue Priority',
				'width': 200
			})

		self.statuses = ['Open', 'Replied', 'On Hold', 'Resolved', 'Closed']
		for status in self.statuses:
			self.columns.append({
				'label': _(status),
				'fieldname': scrub(status),
				'fieldtype': 'Int',
				'width': 80
			})

		self.columns.append({
			'label': _('Total Issues'),
			'fieldname': 'total_issues',
			'fieldtype': 'Int',
			'width': 100
		})

		self.sla_status_map = {
			'SLA Failed': 'failed',
			'SLA Fulfilled': 'fulfilled',
			'SLA Ongoing': 'ongoing'
		}

		for label, fieldname in self.sla_status_map.items():
			self.columns.append({
				'label': _(label),
				'fieldname': fieldname,
				'fieldtype': 'Int',
				'width': 100
			})

		self.metrics = ['Avg First Response Time', 'Avg Response Time', 'Avg Hold Time',
			'Avg Resolution Time', 'Avg User Resolution Time']

		for metric in self.metrics:
			self.columns.append({
				'label': _(metric),
				'fieldname': scrub(metric),
				'fieldtype': 'Duration',
				'width': 170
			})

	def get_data(self):
		self.get_issues()
		self.get_rows()

	def get_issues(self):
		filters = self.get_common_filters()
		self.field_map = {
			'Customer': 'customer',
			'Issue Type': 'issue_type',
			'Issue Priority': 'priority',
			'Assigned To': '_assign'
		}

		self.entries = frappe.db.get_all('Issue',
			fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date', 'status', 'avg_response_time',
				'first_response_time', 'total_hold_time', 'user_resolution_time', 'resolution_time', 'agreement_status'],
			filters=filters
		)

	def get_common_filters(self):
		filters = {}
		filters['opening_date'] = ('between', [self.filters.from_date, self.filters.to_date])

		if self.filters.get('assigned_to'):
			filters['_assign'] = ('like', '%' + self.filters.get('assigned_to') + '%')

		for entry in ['company', 'status', 'priority', 'customer', 'project']:
			if self.filters.get(entry):
				filters[entry] = self.filters.get(entry)

		return filters

	def get_rows(self):
		self.data = []
		self.get_summary_data()

		for entity, data in self.issue_summary_data.items():
			if self.filters.based_on == 'Customer':
				row = {'customer': entity}
			elif self.filters.based_on == 'Assigned To':
				row = {'user': entity}
			elif self.filters.based_on == 'Issue Type':
				row = {'issue_type': entity}
			elif self.filters.based_on == 'Issue Priority':
				row = {'priority': entity}

			for status in self.statuses:
				count = flt(data.get(status, 0.0))
				row[scrub(status)] = count

			row['total_issues'] = data.get('total_issues', 0.0)

			for sla_status in self.sla_status_map.values():
				value = flt(data.get(sla_status), 0.0)
				row[sla_status] = value

			for metric in self.metrics:
				value = flt(data.get(scrub(metric)), 0.0)
				row[scrub(metric)] = value

			self.data.append(row)

	def get_summary_data(self):
		self.issue_summary_data = frappe._dict()

		for d in self.entries:
			status = d.status
			agreement_status = scrub(d.agreement_status)

			if self.filters.based_on == 'Assigned To':
				if d._assign:
					for entry in json.loads(d._assign):
						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(status, 0.0)
						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(agreement_status, 0.0)
						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault('total_issues', 0.0)
						self.issue_summary_data[entry][status] += 1
						self.issue_summary_data[entry][agreement_status] += 1
						self.issue_summary_data[entry]['total_issues'] += 1

			else:
				field = self.field_map.get(self.filters.based_on)
				value = d.get(field)
				if not value:
					value = _('Not Specified')

				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(status, 0.0)
				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(agreement_status, 0.0)
				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault('total_issues', 0.0)
				self.issue_summary_data[value][status] += 1
				self.issue_summary_data[value][agreement_status] += 1
				self.issue_summary_data[value]['total_issues'] += 1

		self.get_metrics_data()

	def get_metrics_data(self):
		issues = []

		metrics_list = ['avg_response_time', 'avg_first_response_time', 'avg_hold_time',
			'avg_resolution_time', 'avg_user_resolution_time']

		for entry in self.entries:
			issues.append(entry.name)

		field = self.field_map.get(self.filters.based_on)

		if issues:
			if self.filters.based_on == 'Assigned To':
				assignment_map = frappe._dict()
				for d in self.entries:
					if d._assign:
						for entry in json.loads(d._assign):
							for metric in metrics_list:
								self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(metric, 0.0)

							self.issue_summary_data[entry]['avg_response_time'] += d.get('avg_response_time') or 0.0
							self.issue_summary_data[entry]['avg_first_response_time'] += d.get('first_response_time') or 0.0
							self.issue_summary_data[entry]['avg_hold_time'] += d.get('total_hold_time') or 0.0
							self.issue_summary_data[entry]['avg_resolution_time'] += d.get('resolution_time') or 0.0
							self.issue_summary_data[entry]['avg_user_resolution_time'] += d.get('user_resolution_time') or 0.0

							if not assignment_map.get(entry):
								assignment_map[entry] = 0
							assignment_map[entry] += 1

				for entry in assignment_map:
					for metric in metrics_list:
						self.issue_summary_data[entry][metric] /= flt(assignment_map.get(entry))

			else:
				data = frappe.db.sql("""
					SELECT
						{0}, AVG(first_response_time) as avg_frt,
						AVG(avg_response_time) as avg_resp_time,
						AVG(total_hold_time) as avg_hold_time,
						AVG(resolution_time) as avg_resolution_time,
						AVG(user_resolution_time) as avg_user_resolution_time
					FROM `tabIssue`
					WHERE
						name IN %(issues)s
					GROUP BY {0}
				""".format(field), {'issues': issues}, as_dict=1)

				for entry in data:
					value = entry.get(field)
					if not value:
						value = _('Not Specified')

					for metric in metrics_list:
						self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(metric, 0.0)

					self.issue_summary_data[value]['avg_response_time'] = entry.get('avg_resp_time') or 0.0
					self.issue_summary_data[value]['avg_first_response_time'] = entry.get('avg_frt') or 0.0
					self.issue_summary_data[value]['avg_hold_time'] = entry.get('avg_hold_time') or 0.0
					self.issue_summary_data[value]['avg_resolution_time'] = entry.get('avg_resolution_time') or 0.0
					self.issue_summary_data[value]['avg_user_resolution_time'] = entry.get('avg_user_resolution_time') or 0.0

	def get_chart_data(self):
		self.chart = []

		labels = []
		open_issues = []
		replied_issues = []
		on_hold_issues = []
		resolved_issues = []
		closed_issues = []

		entity = self.filters.based_on
		entity_field = self.field_map.get(entity)
		if entity == 'Assigned To':
			entity_field = 'user'

		for entry in self.data:
			labels.append(entry.get(entity_field))
			open_issues.append(entry.get('open'))
			replied_issues.append(entry.get('replied'))
			on_hold_issues.append(entry.get('on_hold'))
			resolved_issues.append(entry.get('resolved'))
			closed_issues.append(entry.get('closed'))

		self.chart = {
			'data': {
				'labels': labels[:30],
				'datasets': [
					{
						'name': 'Open',
						'values': open_issues[:30]
					},
					{
						'name': 'Replied',
						'values': replied_issues[:30]
					},
					{
						'name': 'On Hold',
						'values': on_hold_issues[:30]
					},
					{
						'name': 'Resolved',
						'values': resolved_issues[:30]
					},
					{
						'name': 'Closed',
						'values': closed_issues[:30]
					}
				]
			},
			'type': 'bar',
			'barOptions': {
				'stacked': True
			}
		}

	def get_report_summary(self):
		self.report_summary = []

		open_issues = 0
		replied = 0
		on_hold = 0
		resolved = 0
		closed = 0

		for entry in self.data:
			open_issues += entry.get('open')
			replied += entry.get('replied')
			on_hold += entry.get('on_hold')
			resolved += entry.get('resolved')
			closed += entry.get('closed')

		self.report_summary = [
			{
				'value': open_issues,
				'indicator': 'Red',
				'label': _('Open'),
				'datatype': 'Int',
			},
			{
				'value': replied,
				'indicator': 'Grey',
				'label': _('Replied'),
				'datatype': 'Int',
			},
			{
				'value': on_hold,
				'indicator': 'Grey',
				'label': _('On Hold'),
				'datatype': 'Int',
			},
			{
				'value': resolved,
				'indicator': 'Green',
				'label': _('Resolved'),
				'datatype': 'Int',
			},
			{
				'value': closed,
				'indicator': 'Green',
				'label': _('Closed'),
				'datatype': 'Int',
			}
		]
